package cn.example.converter;

import com.alibaba.druid.util.JdbcConstants;
import lombok.extern.slf4j.Slf4j;
import org.apache.ibatis.executor.statement.StatementHandler;
import org.apache.ibatis.plugin.Interceptor;
import org.apache.ibatis.plugin.Intercepts;
import org.apache.ibatis.plugin.Invocation;
import org.apache.ibatis.plugin.Plugin;
import org.apache.ibatis.plugin.Signature;
import org.springframework.beans.factory.annotation.Value;

import java.lang.reflect.Field;
import java.sql.Connection;
import java.util.List;
import java.util.Optional;
import java.util.stream.Stream;

/**
 * @Auther: CongKe
 * @Date: 2025/5/15 16:17
 * @Description: BaseConverter
 */
@Slf4j(topic = "sql-adapter")
@Intercepts({
        @Signature(type = StatementHandler.class, method = "prepare", args = {Connection.class, Integer.class})
})
public class SqlAdapter implements Interceptor {

    /**
     * 转换器集合
     */
    private static final List<BaseConverter> CONVERTERS = Stream.of(new DamengConverter(),
                    new GBaseConverter(),
                    new KingBaseConverter()).
            toList();


    @Value("${pass.dbType}:dm")
    private String dbType;

    /**
     * 拦截翻译
     *
     * @param invocation 拦截对象
     * @return Object
     * @throws Throwable 异常
     */
    @Override
    public Object intercept(Invocation invocation) throws Throwable {
        StatementHandler statementHandler = (StatementHandler) invocation.getTarget();
        String originalSql = statementHandler.getBoundSql().getSql();

        // 获取数据库连接
        Connection connection = (Connection) invocation.getArgs()[0];

        // 获取数据库逻辑容器信息
        String databaseProductName = connection.getMetaData().getDatabaseProductName();

        // 在这里对 SQL 进行重写
        String rewrittenSql = adapterSql(databaseProductName, originalSql);
        // 使用反射将重写后的 SQL 设置回去
        Field field = statementHandler.getBoundSql().getClass().getDeclaredField("sql");
        field.setAccessible(true);
        field.set(statementHandler.getBoundSql(), rewrittenSql);
        return invocation.proceed();
    }

    /**
     * sql 适配
     *
     * @param originalSql 原始sql
     * @return 适配结果
     */
    private String adapterSql(String database, String originalSql) {
        long start = System.currentTimeMillis();
        Optional<BaseConverter> converter = CONVERTERS.stream().filter(o -> o.support(dbType)).findFirst();

        if (converter.isPresent()) {
            List<String> result = converter.get().convert(database, originalSql);
            originalSql = String.join(";", result);
        }

        log.info("SQL 转换用时：{} ms", (System.currentTimeMillis() - start));
        return originalSql;
    }

    @Override
    public Object plugin(Object target) {
        return Plugin.wrap(target, this);
    }


    public static void main(String[] args) {
        String sql = "SELECT \n" +
                "    u.id AS user_id,\n" +
                "    u.name AS user_name,\n" +
                "    COUNT(o.id) AS total_orders,\n" +
                "    SUM(o.amount) AS total_amount,\n" +
                "    IFNULL(u.email, 'no_email') AS email,\n" +
                "    DATE_FORMAT(u.created_at, '%Y-%m') AS reg_month\n" +
                "FROM users u\n" +
                "LEFT JOIN orders o ON u.id = o.user_id\n" +
                "WHERE u.status = 1\n" +
                "GROUP BY u.id, u.name\n" +
                "HAVING total_orders > 5 AND total_amount > 1000\n" +
                "ORDER BY total_amount DESC\n" +
                "LIMIT 10 OFFSET 20;";
        long start = System.currentTimeMillis();
        String dbType = JdbcConstants.KINGBASE.name();
        String schemaName = " ";

        Optional<BaseConverter> converter = CONVERTERS.stream().filter(o -> o.support(dbType)).findFirst();

        if (converter.isPresent()) {
            List<String> result = converter.get().convert(schemaName, sql);
            result.forEach(log::info);
        }

        log.info("SQL 转换用时：{} ms", (System.currentTimeMillis() - start));

    }

}
